﻿// Decompiled with JetBrains decompiler
// Type: TaleWorlds.MountAndBlade.View.MissionViews.MissionMainAgentInteractionComponent
// Assembly: TaleWorlds.MountAndBlade.View, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: 61152EB8-513B-4D4A-8AC0-0D1465C6CF6C
// Assembly location: D:\steam\steamapps\common\Mount & Blade II Bannerlord\Modules\Native\bin\Win64_Shipping_Client\TaleWorlds.MountAndBlade.View.dll

using System;
using System.Linq;
using TaleWorlds.Core;
using TaleWorlds.DotNet;
using TaleWorlds.Engine;
using TaleWorlds.Library;
using TaleWorlds.MountAndBlade.View.Screens;

#nullable disable
namespace TaleWorlds.MountAndBlade.View.MissionViews
{
  public class MissionMainAgentInteractionComponent
  {
    private IFocusable _currentInteractableObject;
    private readonly MissionMainAgentController _mainAgentController;

    public event MissionMainAgentInteractionComponent.MissionFocusGainedEventDelegate OnFocusGained;

    public event MissionMainAgentInteractionComponent.MissionFocusLostEventDelegate OnFocusLost;

    public event MissionMainAgentInteractionComponent.MissionFocusHealthChangeDelegate OnFocusHealthChanged;

    public IFocusable CurrentFocusedObject { get; private set; }

    public IFocusable CurrentFocusedMachine { get; private set; }

    public void SetCurrentFocusedObject(
      IFocusable focusedObject,
      IFocusable focusedMachine,
      bool isInteractable)
    {
      if (this.CurrentFocusedObject != null && (this.CurrentFocusedObject != focusedObject || this._currentInteractableObject != null && !isInteractable || this._currentInteractableObject == null & isInteractable))
      {
        this.FocusLost(this.CurrentFocusedObject, this.CurrentFocusedMachine);
        this._currentInteractableObject = (IFocusable) null;
        this.CurrentFocusedObject = (IFocusable) null;
        this.CurrentFocusedMachine = (IFocusable) null;
      }
      if (this.CurrentFocusedObject != null || focusedObject == null)
        return;
      if (focusedObject != this.CurrentFocusedObject)
        this.FocusGained(focusedObject, focusedMachine, isInteractable);
      if (isInteractable)
        this._currentInteractableObject = focusedObject;
      this.CurrentFocusedObject = focusedObject;
      this.CurrentFocusedMachine = focusedMachine;
    }

    public void ClearFocus()
    {
      if (this.CurrentFocusedObject != null)
        this.FocusLost(this.CurrentFocusedObject, this.CurrentFocusedMachine);
      this._currentInteractableObject = (IFocusable) null;
      this.CurrentFocusedObject = (IFocusable) null;
      this.CurrentFocusedMachine = (IFocusable) null;
    }

    public void OnClearScene() => this.ClearFocus();

    private Mission CurrentMission => this._mainAgentController.Mission;

    private MissionScreen CurrentMissionScreen => this._mainAgentController.MissionScreen;

    private Scene CurrentMissionScene => this._mainAgentController.Mission.Scene;

    public MissionMainAgentInteractionComponent(MissionMainAgentController mainAgentController)
    {
      this._mainAgentController = mainAgentController;
    }

    private static float GetCollisionDistanceSquaredOfIntersectionFromMainAgentEye(
      Vec3 rayStartPoint,
      Vec3 rayDirection,
      float rayLength)
    {
      float fromMainAgentEye = rayLength * rayLength;
      Vec3 v = rayStartPoint + rayDirection * rayLength;
      Vec3 position = Agent.Main.Position;
      float eyeGlobalHeight = Agent.Main.GetEyeGlobalHeight();
      Vec3 vec3 = new Vec3(position.x, position.y, position.z + eyeGlobalHeight);
      float num1 = v.z - vec3.z;
      if ((double) num1 < 0.0)
      {
        float num2 = MBMath.ClampFloat(-num1, 0.0f, (float) ((Agent.Main.HasMount ? (double) eyeGlobalHeight - (double) Agent.Main.MountAgent.GetEyeGlobalHeight() : (double) eyeGlobalHeight) * 0.75));
        vec3.z -= num2;
        fromMainAgentEye = vec3.DistanceSquared(v);
      }
      return fromMainAgentEye;
    }

    private void FocusGained(
      IFocusable focusedObject,
      IFocusable focusedMachine,
      bool isInteractable)
    {
      focusedObject.OnFocusGain(Agent.Main);
      focusedMachine?.OnFocusGain(Agent.Main);
      foreach (MissionBehavior missionBehavior in this.CurrentMission.MissionBehaviors)
        missionBehavior.OnFocusGained(Agent.Main, focusedObject, isInteractable);
      MissionMainAgentInteractionComponent.MissionFocusGainedEventDelegate onFocusGained = this.OnFocusGained;
      if (onFocusGained == null)
        return;
      onFocusGained(Agent.Main, this.CurrentFocusedObject, isInteractable);
    }

    private void FocusLost(IFocusable focusedObject, IFocusable focusedMachine)
    {
      focusedObject.OnFocusLose(Agent.Main);
      focusedMachine?.OnFocusLose(Agent.Main);
      foreach (MissionBehavior missionBehavior in this.CurrentMission.MissionBehaviors)
        missionBehavior.OnFocusLost(Agent.Main, focusedObject);
      MissionMainAgentInteractionComponent.MissionFocusLostEventDelegate onFocusLost = this.OnFocusLost;
      if (onFocusLost == null)
        return;
      onFocusLost(Agent.Main, this.CurrentFocusedObject);
    }

    public void FocusTick()
    {
      IFocusable focusedObject = (IFocusable) null;
      UsableMachine focusedMachine = (UsableMachine) null;
      bool flag1 = true;
      bool flag2 = true;
      if (Mission.Current.Mode != MissionMode.Conversation && Mission.Current.Mode != MissionMode.CutScene)
      {
        Agent main = Agent.Main;
        if (!this.CurrentMissionScreen.SceneLayer.Input.IsGameKeyDown(25) && main != null && main.IsOnLand())
        {
          float num1 = 10f;
          Vec3 direction = this.CurrentMissionScreen.CombatCamera.Direction;
          Vec3 rayDirection = direction;
          Vec3 position1 = this.CurrentMissionScreen.CombatCamera.Position;
          Vec3 position2 = main.Position;
          float num2 = new Vec3(position1.x, position1.y).Distance(new Vec3(position2.x, position2.y));
          Vec3 vec3 = position1 * (1f - num2) + (position1 + direction) * num2;
          float collisionDistance;
          if (this.CurrentMissionScene.RayCastForClosestEntityOrTerrain(vec3, vec3 + rayDirection * num1, out collisionDistance, excludeBodyFlags: BodyFlags.CommonFlagsThatDoNotBlocksRay))
            num1 = collisionDistance;
          float num3 = float.MaxValue;
          Agent otherAgent = this.CurrentMission.RayCastForClosestAgent(vec3, vec3 + rayDirection * (num1 + 0.01f), out collisionDistance, main.Index, 0.3f);
          if (otherAgent != null && (!otherAgent.IsMount || otherAgent.RiderAgent == null && main.MountAgent == null && main.CanReachAgent(otherAgent)))
          {
            num3 = collisionDistance;
            focusedObject = (IFocusable) otherAgent;
            if (!main.CanInteractWithAgent(otherAgent, this.CurrentMissionScreen.CameraElevation))
              flag2 = false;
          }
          float num4 = 3f;
          float num5 = num1 + 0.1f;
          GameEntity collidedEntity;
          if (this.CurrentMissionScene.RayCastForClosestEntityOrTerrain(vec3, vec3 + rayDirection * num5, out collisionDistance, out collidedEntity, 0.2f) && (NativeObject) collidedEntity != (NativeObject) null && (double) collisionDistance < (double) num3 || this.CurrentMissionScene.RayCastForClosestEntityOrTerrain(vec3, vec3 + rayDirection * num5, out collisionDistance, out collidedEntity, 0.2f * num4) && (NativeObject) collidedEntity != (NativeObject) null && (double) collisionDistance < (double) num3)
          {
            while (!collidedEntity.GetScriptComponents().Any<ScriptComponentBehavior>((Func<ScriptComponentBehavior, bool>) (sc => sc is IFocusable)) && (NativeObject) collidedEntity.Parent != (NativeObject) null)
              collidedEntity = collidedEntity.Parent;
            focusedMachine = collidedEntity.GetFirstScriptOfType<UsableMachine>();
            if (focusedMachine != null && !focusedMachine.IsDisabled)
            {
              GameEntity standingPointForAgent = focusedMachine.GetValidStandingPointForAgent(main);
              if ((NativeObject) standingPointForAgent != (NativeObject) null)
                collidedEntity = standingPointForAgent;
            }
            flag1 = false;
            UsableMissionObject firstScriptOfType = collidedEntity.GetFirstScriptOfType<UsableMissionObject>();
            if (firstScriptOfType is SpawnedItemEntity)
            {
              if (this.CurrentMission.IsMainAgentItemInteractionEnabled && main.CanReachObject(firstScriptOfType, MissionMainAgentInteractionComponent.GetCollisionDistanceSquaredOfIntersectionFromMainAgentEye(vec3, rayDirection, collisionDistance)))
              {
                focusedObject = (IFocusable) firstScriptOfType;
                if (main.CanUseObject(firstScriptOfType))
                  flag1 = true;
              }
            }
            else if (firstScriptOfType != null)
            {
              focusedObject = (IFocusable) firstScriptOfType;
              if (this.CurrentMission.IsMainAgentObjectInteractionEnabled && !main.IsUsingGameObject && main.IsOnLand() && main.ObjectHasVacantPosition(firstScriptOfType))
                flag1 = true;
            }
            else if (focusedMachine != null)
              focusedObject = (IFocusable) focusedMachine;
            else if (collidedEntity.GetScriptComponents().FirstOrDefault<ScriptComponentBehavior>((Func<ScriptComponentBehavior, bool>) (sc => sc is IFocusable)) is IFocusable focusable)
              focusedObject = focusable;
          }
          if ((focusedObject == null || !flag1) && main.MountAgent != null && main.CanInteractWithAgent(main.MountAgent, this.CurrentMissionScreen.CameraElevation))
          {
            focusedObject = (IFocusable) main.MountAgent;
            flag1 = true;
          }
        }
        if (focusedObject == null)
        {
          this.ClearFocus();
        }
        else
        {
          bool isInteractable = focusedObject is Agent ? flag2 : flag1;
          this.SetCurrentFocusedObject(focusedObject, (IFocusable) focusedMachine, isInteractable);
        }
      }
      else
      {
        if (this.CurrentFocusedObject == null || Mission.Current.Mode == MissionMode.Conversation)
          return;
        this.ClearFocus();
      }
    }

    public void FocusStateCheckTick()
    {
      if (!this.CurrentMissionScreen.SceneLayer.Input.IsGameKeyPressed(13) || !this.CurrentMission.IsMainAgentObjectInteractionEnabled && !this.IsFocusMountable() || this.CurrentMission.IsOrderMenuOpen)
        return;
      Agent main = Agent.Main;
      if (this._currentInteractableObject is UsableMissionObject interactableObject1)
      {
        if (main.IsUsingGameObject || !main.IsOnLand() || interactableObject1 is SpawnedItemEntity || !main.ObjectHasVacantPosition(interactableObject1))
          return;
        main.HandleStartUsingAction(interactableObject1, -1);
      }
      else
      {
        Agent interactableObject = this._currentInteractableObject as Agent;
        if (main.IsOnLand() && interactableObject != null)
        {
          interactableObject.OnUse(main);
        }
        else
        {
          if (!main.IsUsingGameObject || main.CurrentlyUsedGameObject is SpawnedItemEntity || interactableObject != null && main.CurrentlyUsedGameObject is StandingPoint currentlyUsedGameObject && currentlyUsedGameObject.PlayerStopsUsingWhenInteractsWithOther)
            return;
          main.HandleStopUsingAction();
          this.ClearFocus();
        }
      }
    }

    private bool IsFocusMountable()
    {
      return this._currentInteractableObject is Agent interactableObject && interactableObject.IsMount;
    }

    public void FocusedItemHealthTick()
    {
      if (this.CurrentFocusedObject is UsableMissionObject currentFocusedObject2)
      {
        GameEntity gameEntity = currentFocusedObject2.GameEntity;
        while ((NativeObject) gameEntity != (NativeObject) null && !gameEntity.HasScriptOfType<UsableMachine>())
          gameEntity = gameEntity.Parent;
        if (!((NativeObject) gameEntity != (NativeObject) null))
          return;
        UsableMachine firstScriptOfType = gameEntity.GetFirstScriptOfType<UsableMachine>();
        if (firstScriptOfType?.DestructionComponent == null)
          return;
        MissionMainAgentInteractionComponent.MissionFocusHealthChangeDelegate focusHealthChanged = this.OnFocusHealthChanged;
        if (focusHealthChanged == null)
          return;
        focusHealthChanged(this.CurrentFocusedObject, firstScriptOfType.DestructionComponent.HitPoint / firstScriptOfType.DestructionComponent.MaxHitPoint, true);
      }
      else if (this.CurrentFocusedObject is UsableMachine currentFocusedObject1)
      {
        if (currentFocusedObject1.DestructionComponent == null)
          return;
        MissionMainAgentInteractionComponent.MissionFocusHealthChangeDelegate focusHealthChanged = this.OnFocusHealthChanged;
        if (focusHealthChanged == null)
          return;
        focusHealthChanged(this.CurrentFocusedObject, currentFocusedObject1.DestructionComponent.HitPoint / currentFocusedObject1.DestructionComponent.MaxHitPoint, true);
      }
      else
      {
        if (!(this.CurrentFocusedObject is DestructableComponent currentFocusedObject))
          return;
        MissionMainAgentInteractionComponent.MissionFocusHealthChangeDelegate focusHealthChanged = this.OnFocusHealthChanged;
        if (focusHealthChanged == null)
          return;
        focusHealthChanged(this.CurrentFocusedObject, currentFocusedObject.HitPoint / currentFocusedObject.MaxHitPoint, true);
      }
    }

    public delegate void MissionFocusGainedEventDelegate(
      Agent agent,
      IFocusable focusableObject,
      bool isInteractable);

    public delegate void MissionFocusLostEventDelegate(Agent agent, IFocusable focusableObject);

    public delegate void MissionFocusHealthChangeDelegate(
      IFocusable focusable,
      float healthPercentage,
      bool hideHealthbarWhenFull);
  }
}
